package com.gitee.wsl.compose.ui.richtext.edit.model.span.style

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.unit.isUnspecified
import androidx.compose.ui.unit.sp
import com.gitee.wsl.annotation.ExperimentalApi
import com.gitee.wsl.compose.image.LocalImageLoader
import com.gitee.wsl.compose.ui.richtext.edit.annotation.ExperimentalRichTextApi
import com.gitee.wsl.compose.ui.richtext.edit.state.RichTextState
import com.gitee.wsl.compose.ui.richtext.edit.state.updateAnnotatedString
import kotlin.collections.set


@OptIn(ExperimentalRichTextApi::class)
class Image(
    val model: Any,
    width: TextUnit,
    height: TextUnit,
    val contentDescription: String? = null,
) : RichSpanStyle {

    init {
        require(width.isSpecified || height.isSpecified) {
            "At least one of the width or height should be specified"
        }

        require(width.value >= 0 || height.value >= 0) {
            "The width and height should be greater than or equal to 0"
        }

        require(width.value.isFinite() || height.value.isFinite()) {
            "The width and height should be finite"
        }
    }

     var width: TextUnit by mutableStateOf(width)
        private set

     var height: TextUnit by mutableStateOf(height)
        private set

    private val id get() = "$model-${width.value}-${height.value}"

    //override val spanStyle: (RichTextConfig) -> SpanStyle = { SpanStyle() }

    override fun AnnotatedString.Builder.appendCustomContent(
        richTextState: RichTextState
    ): AnnotatedString.Builder {
        if (id !in richTextState.inlineContentMap.keys) {
            richTextState.inlineContentMap[id] = createInlineTextContent(richTextState = richTextState)
        }

        richTextState.usedInlineContentMapKeys.add(id)

        appendInlineContent(id = id)

        return this
    }

    @OptIn(ExperimentalApi::class)
    private fun createInlineTextContent(
        richTextState: RichTextState
    ): InlineTextContent =
        InlineTextContent(
            placeholder = Placeholder(
                width = width.value.coerceAtLeast(0f).sp,
                height = height.value.coerceAtLeast(0f).sp,
                placeholderVerticalAlign = PlaceholderVerticalAlign.TextBottom
            ),
            children = {
                val density = LocalDensity.current
                val imageLoader = LocalImageLoader.current
                val data = imageLoader.load(model) ?: return@InlineTextContent

                LaunchedEffect(id, data) {
                    if (data.painter.intrinsicSize.isUnspecified)
                        return@LaunchedEffect

                    val newWidth = with(density) {
                        data.painter.intrinsicSize.width.coerceAtLeast(0f).toSp()
                    }
                    val newHeight = with(density) {
                        data.painter.intrinsicSize.height.coerceAtLeast(0f).toSp()
                    }

                    if (width == newWidth && height == newHeight)
                        return@LaunchedEffect

                    richTextState.inlineContentMap.remove(id)

                    if (width.isUnspecified || width.value <= 0)
                        width = newWidth

                    if (height.isUnspecified || height.value <= 0)
                        height = newHeight

                    richTextState.inlineContentMap[id] = createInlineTextContent(richTextState = richTextState)
                    richTextState.updateAnnotatedString()
                }

                Image(
                    painter = data.painter,
                    contentDescription = data.contentDescription ?: contentDescription,
                    alignment = data.alignment,
                    contentScale = data.contentScale,
                    modifier = data.modifier
                        .fillMaxSize()
                )
            }
        )


    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Image) return false

        if (model != other.model) return false
        if (width != other.width) return false
        if (height != other.height) return false

        return true
    }

    override fun hashCode(): Int {
        var result = model.hashCode()
        result = 31 * result + width.hashCode()
        result = 31 * result + height.hashCode()
        return result
    }
}

@OptIn(ExperimentalRichTextApi::class)
fun RichSpanStyle.image(model: Any,
                        width: TextUnit,
                        height: TextUnit,
                        contentDescription: String? = null
              ) =  Image(model, width, height, contentDescription)